/**  
 * @Title LoginController.java
 * @date 2016年3月2日 下午5:54:45
 * @Copyright (c) 2016, unibroad.com Inc. All rights reserved.
 */
package com.strugglerz.web.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Sha1Hash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.strugglerz.biz.service.ISysUserService;
import com.strugglerz.model.common.BaseResult;
import com.strugglerz.model.common.CommonConstants;
import com.strugglerz.model.common.SysConstants;
import com.strugglerz.model.dalentity.SysUser;
import com.strugglerz.model.exception.BizHandleException;

/**
 * @Description 登录控制
 * @Class com.unibroad.touchus.admin.web.biz.controller.LoginController
 * @author suan@unibroad.com
 * @version V1.0
 */
@Controller
@RequestMapping("/auth")
public class AuthController {

	/** logger */
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private ISysUserService<SysUser> sysUserService;

    /**
     * 用户登录
     * 
     * @param modelMap
     * @param request
     * @return
     */
    @RequestMapping(value = "/login.html")
    public String doLogin(ModelMap modelMap, HttpServletRequest request, String userName, String password,
            String code, String rememberMe) {
    	
        try {

            Subject currentUser = SecurityUtils.getSubject();
            Session session = currentUser.getSession();
            String zs = (String) session.getAttribute(SysConstants.SESSION_SECURITY_CODE);
            modelMap.put("userName", userName);
            modelMap.put("password", password);
            modelMap.put("rememberMe", rememberMe);

            Assert.isTrue(
                    StringUtils.isNotBlank(code) && StringUtils.equalsIgnoreCase(code,
                            (String) session.getAttribute(SysConstants.SESSION_SECURITY_CODE)),
                    "登录失败,验证码不正确!");

            Assert.isTrue(StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(password),
                    "登录失败,用户密码不正确!");

            UsernamePasswordToken token = new UsernamePasswordToken(userName, encryptPwd(password),
                    Boolean.parseBoolean(rememberMe));
            currentUser.login(token);
            token.clear();

            if (StringUtils.isNotBlank(rememberMe)) {
                token.setRememberMe(true);
            }

            request.getSession().setAttribute("userName", userName);
            SysUser user = sysUserService.queryByName(userName);
            request.getSession().setAttribute("userId", user.getId());

            logger.info("用户登录成功,用户名: " + userName);

            // return "/index";
            // return "forward:/index.html";

            // return "forward:/hello" => 转发到能够匹配 /hello 的 controller 上
            // return "hello" => 实际上还是转发，只不过是框架会找到该逻辑视图名对应的 View 并渲染
            // return "/hello" => 同 return "hello"

            modelMap.clear();
            return "redirect:/index.html";

        } catch (LockedAccountException lae) {
            logger.error("login exception " + modelMap, lae);
            modelMap.put(CommonConstants.ERROR_MSG_TIP_KEY, "登录失败,用户状态异常!");
        } catch (IllegalArgumentException e) {
            logger.error("login exception " + modelMap, e);
            modelMap.put(CommonConstants.ERROR_MSG_TIP_KEY,
                    e instanceof IllegalArgumentException ? e.getMessage() : "登录失败,请检查密码是否准确!");
        } catch (Exception ex) {
            logger.error("login exception " + modelMap, ex);
            modelMap.put(CommonConstants.ERROR_MSG_TIP_KEY, "登录失败,系统异常!");
        }

        return "/auth/login";
    }

    /**
     * 用户登出
     * 
     * @param modelMap
     * @param request
     */
    @RequestMapping(value = "/logout.html")
    public String logout(ModelMap modelMap, HttpServletRequest request) {

        try {
            Subject currentUser = SecurityUtils.getSubject();
            currentUser.logout();
            return "redirect:/auth/login.html";
        } catch (Exception ae) {
            ae.printStackTrace();
        }

        return "redirect:/index.html";
    }

    @RequestMapping(value = "/changePwd.json")
    public @ResponseBody BaseResult<Object> changePwd(final HttpServletRequest request, final String userName,
            final String originPwd, final String newPwd, final String confirmPwd) {

        Assert.isTrue(StringUtils.isNotBlank(userName), "操作失败,参数不正确!");
        Assert.isTrue(StringUtils.isNotBlank(newPwd) && StringUtils.equals(newPwd, confirmPwd),
                "操作失败,两次密码输入不一致!");

        BaseResult<Object> result = new BaseResult<Object>(false);

        try {
            SysUser sysUser = sysUserService.queryByName(userName);
            Assert.isTrue(isUserPwdMatched(userName, originPwd, sysUser.getPassword()), "操作失败,密码不正确!");
            sysUser.setPassword(encryptPwd(confirmPwd));
            result.setResult(true, "操作成功", sysUserService.update(sysUser));
        } catch (BizHandleException e) {
            result.setMessage(e.getMessage());
        }

        return result;
    }

    @RequestMapping("/code")
    public void code(HttpServletResponse response) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        String code = drawImg(output);

        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        session.setAttribute(SysConstants.SESSION_SECURITY_CODE, code);

        try {
            ServletOutputStream out = response.getOutputStream();
            output.writeTo(out);
        } catch (IOException e) {
            logger.error("get code for login exception", e);
        }
    }

    private String drawImg(ByteArrayOutputStream output) {
        String code = "";
        for (int i = 0; i < 4; i++) {
            code += randomChar();
        }
        int width = 70;
        int height = 25;
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
        Font font = new Font("Times New Roman", Font.PLAIN, 20);
        Graphics2D g = bi.createGraphics();
        g.setFont(font);
        Color color = new Color(66, 2, 82);
        g.setColor(color);
        g.setBackground(new Color(226, 226, 240));
        g.clearRect(0, 0, width, height);
        FontRenderContext context = g.getFontRenderContext();
        Rectangle2D bounds = font.getStringBounds(code, context);
        double x = (width - bounds.getWidth()) / 2;
        double y = (height - bounds.getHeight()) / 2;
        double ascent = bounds.getY();
        double baseY = y - ascent;
        g.drawString(code, (int) x, (int) baseY);
        g.dispose();
        try {
            ImageIO.write(bi, "jpg", output);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return code;
    }

    private char randomChar() {
        Random r = new Random();
        String s = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789";
        return s.charAt(r.nextInt(s.length()));
    }

    private String encryptPwd(String pwd) {
        return new Sha1Hash(pwd).toHex();
    }

    /**
     * 校验是否正确的密码
     * 
     * @param userName
     *            用户名称
     * @param userPwd
     *            密码明文
     * @param encrypt
     *            密码密文
     * @return 是否正确的密码
     */
    private boolean isUserPwdMatched(String userName, String userPwd, String encrypt) {
        // 空密码，视为正确
        if (StringUtils.isNotBlank(userName) && StringUtils.isBlank(userPwd)
                && StringUtils.isBlank(encrypt)) {
            return true;
        }
        Sha1Hash hashedPwd = Sha1Hash.fromHexString(encrypt);
        AuthenticationInfo account = new SimpleAuthenticationInfo(SecurityUtils.getSubject().getPrincipals(),
                hashedPwd.getBytes());
        AuthenticationToken token = new UsernamePasswordToken(userName, userPwd);
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName(Sha1Hash.ALGORITHM_NAME);
        return matcher.doCredentialsMatch(token, account);
    }
}
